import csv
import os
import random
import time

import chardet
from moviepy import VideoFileClip, AudioFileClip

from combinations.config import BASE_DIR, MATERIAL_MAPPING, COMBO_NAME_PREFIX, CUSTOM_SCRIPT
from .draft_handler import DraftHandler
from combinations.conf import logger

from .markdown_generator import MarkdownGenerator
from .rpa.main import export_drafts
from .script import TOB, JOKE
from .utils import generate_combinations


def is_media_file_corrupted(file_path):
    """
    检查音视频文件是否损坏（仅检查头部信息，不完全解码）
    :param file_path: 文件路径
    :return: True if corrupted or unreadable, False otherwise
    """
    ext = os.path.splitext(file_path)[1].lower()

    try:
        if ext in ('.mp4', '.avi', '.mov'):
            with VideoFileClip(file_path, audio=False) as clip:
                pass
        elif ext in ('.mp3', '.wav'):
            with AudioFileClip(file_path) as clip:
                pass
        else:
            # 非音视频文件默认视为有效
            return False
        return False
    except Exception as e:
        logger.warning(f"[文件校验] 文件损坏或不可读: {file_path}，错误: {e}")
        try:
            os.remove(file_path)
            logger.info(f"[清理] 已删除损坏文件: {file_path}")
        except Exception as del_e:
            logger.error(f"[错误] 删除损坏文件失败: {file_path}，原因: {del_e}")
        return True


def combination(start=0, end=-1, is_random=False, zi_mu=None, kou_tu=None, enable_delete=False, frame_sampling=False):
    # 获取所有文件
    global df
    file_lists_and_txt = []
    allow_duplicates = []

    # 检查 MATERIAL_MAPPING 中的目录是否存在
    for v in MATERIAL_MAPPING.values():
        folder_path = os.path.join(BASE_DIR, v[0])
        if not os.path.exists(folder_path):
            logger.warning(f"目录 {folder_path} 不存在，跳过扫描")
            continue

    for root, dirs, files in os.walk(BASE_DIR):
        # 对每个子目录下的文件进行扫描
        if not files:
            continue

        for k, v in MATERIAL_MAPPING.items():
            if v[0] == os.path.basename(root):  # 修改为使用文件夹名称匹配
                temp_list = []
                allow_duplicates.append(v[1])
                if "text-track" in k:
                    # todo 此时要读取所有.txt后缀文件，以回车符作为分割，append到file_lists中
                    txt_files = [f for f in files if f.endswith('.txt')]  # 筛选出所有 .txt 文件
                    for txt_file in txt_files:
                        file_path = os.path.join(root, txt_file)  # 获取文件的完整路径
                        with open(file_path, 'r', encoding='utf-8') as f:
                            content = f.read().strip()  # 读取文件内容
                            folder_name = os.path.basename(root)
                            temp_list = temp_list + [f"{folder_name}/{line.strip()}" for line in content.split('\n') if
                                                     line.strip()]
                    # todo 读取csv文件，csv文件可以支持文本换行
                    csv_files = [f for f in files if f.endswith('.csv')]  # 筛选出所有 .csv 文件
                    for csv_file in csv_files:
                        file_path = os.path.join(root, csv_file)  # 获取文件的完整路径
                        # encoding = 'utf-8'
                        with open(file_path, 'rb') as file:
                            raw_data = file.read()
                            result = chardet.detect(raw_data)
                            encoding = result['encoding']

                        with open(file_path, 'r', encoding=encoding) as f:
                            reader = csv.reader(f)
                            folder_name = os.path.basename(root)
                            temp_list = temp_list + [f"{folder_name}/{row[0].strip()}" for row in reader if
                                                     row and row[0].strip()]
                            # temp_list = temp_list + [row[0].strip() for row in reader if row and row[0].strip()]
                else:
                    files = [f for f in files]
                    for file in files:
                        file_path = os.path.join(root, file)
                        # 只对音视频文件进行损坏检测
                        ext = file.split(".")[-1].lower()
                        # if ext in ('.mp4', '.avi', '.mov', '.mp3', '.wav'):
                        #     if is_media_file_corrupted(file_path):
                        #         logger.warning(f"跳过损坏文件: {file_path}")
                        #         continue

                        temp_list.append(file_path)
                random.shuffle(temp_list)
                file_lists_and_txt.append(temp_list)
                logger.info(
                    f"读取 {root},该文件夹包含素材数量为：{len(temp_list)}, {'素材允许重复' if v[1] == 1 else '素材不允许重复'}")
    # 根据MATERIAL_MAPPING定义是否允许重复的文件夹

    # 生成所有组合
    combinations_list = generate_combinations(file_lists_and_txt, allow_duplicates, is_random)

    # 剔除已经存在的组合
    existing_combinations = []
    record_dir = os.path.join(BASE_DIR, "record")  # record目录路径
    prefix_dir = os.path.join(record_dir, COMBO_NAME_PREFIX)  # COMBO_NAME_PREFIX对应的目录

    if os.path.exists(prefix_dir):
        for file_name in os.listdir(prefix_dir):
            if file_name.endswith(".xlsx"):
                file_path = os.path.join(prefix_dir, file_name)
                existing_rows = read_excel_file(file_path)
                existing_combinations.extend(existing_rows)
    logger.info("已存在的组合：")
    logger.info(existing_combinations)
    # 过滤出未存在的组合
    new_combinations_list = compare_combinations(combinations_list, existing_combinations)
    logger.info("未存在的组合：")
    logger.info(new_combinations_list)
    # 假如长度不够则需要加添组合长度
    if end != -1:
        while len(new_combinations_list) <= (end - start):
            logger.info("长度不够，正在添加组合...")
            second_combinations_list = generate_combinations(file_lists_and_txt, allow_duplicates, is_random)
            existing_combinations.extend(new_combinations_list)
            logger.info(f"已使用过的组合长度：{len(existing_combinations)}")
            second_new_combinations_list = compare_combinations(existing_combinations, second_combinations_list)
            logger.info(f"不重复的组合长度：{len(second_new_combinations_list)}")
            new_combinations_list.extend(second_new_combinations_list)
            logger.info(f"新组合长度：{len(new_combinations_list)}")
            if len(new_combinations_list) >= (end - start):
                logger.info("满足长度，添加完成")
                break

    new_combinations_list = new_combinations_list[start:end] if end != -1 else new_combinations_list[start:]
    # # 读取record，COMBO_NAME_PREFIX，目录下的所有excel文件，用combinations_list 与行数据做比对，剔除已经存在行数据。，也就是一摸一样的行数据
    # 然后再将不一样的行数据，赋值给new_combinations_list，md_generator.generate_excel()
    # 生成Markdown表格
    md_generator = MarkdownGenerator(file_dir=os.path.join(COMBO_NAME_PREFIX))
    # md_generator.generate_table(combinations_list)
    md_generator.generate_excel(new_combinations_list)

    # 创建草稿
    draft_handler = DraftHandler()

    if CUSTOM_SCRIPT == "TOB":
        # 加载以 CUSTOM_SCRIPT 命名的py文件在当前目录下的script目录下的py文件
        result = TOB.set_up()
    elif CUSTOM_SCRIPT == "JOKE":
        result = JOKE.set_up()

        # df = trump_scripts_2.custom_script_pre()
    audio_path = ''
    for i, combo in enumerate(new_combinations_list, start=start):
        start_time = time.time()
        combo_name = f"{COMBO_NAME_PREFIX}{i}"
        logger.info(f"组合{i}：{combo}")
        # 根据组合创建草稿
        script = draft_handler.create_draft(combo_name, combo, MATERIAL_MAPPING, kou_tu=kou_tu, )

        # ****** 自定义脚本部分的逻辑 *********
        if CUSTOM_SCRIPT == "TOB":
            script = TOB.run(script, i, result)
        elif CUSTOM_SCRIPT == "JOKE":
            script = JOKE.run(script, i, result)

        # print(script.dumps())
        script.save()

        # 增加组合测试，配置完成后，先测试一个组合生成效果
        # markdown_path = os.path.join("record", COMBO_NAME_PREFIX, "combinations.xlsx")  # 替换为实际的 Markdown 文件路径
        # 从0开始,包含start，不包含end,zi
        # 1,5,10,15,21
        export_drafts(combo_name, zi_mu=zi_mu, kou_tu=kou_tu, audio_path=audio_path, enable_delete=enable_delete,
                      frame_sampling=frame_sampling)
        end_time = time.time()

        print(f"生产1个视频，耗时{(end_time - start_time) / 60}分钟")
    return combinations_list


def read_excel_file(file_path):
    """
    读取Excel文件内容，返回每行的内容列表
    :param file_path: Excel文件路径
    :return: 每行组成的字符串列表
    """
    import pandas as pd
    df = pd.read_excel(file_path, header=None)
    return df.iloc[:, 1:].apply(lambda row: tuple(row.astype(str)), axis=1).tolist()


def compare_combinations(combinations_list, existing_combinations, max_duplicate_ratio=0.2):
    """
    对比组合列表与已存在的组合，返回未重复或重复率低于阈值的新组合
    :param combinations_list: 所有生成的组合
    :param existing_combinations: 已存在的组合
    :param max_duplicate_ratio: 最大允许重复字段比例（默认20%）
    :return: 符合条件的新组合列表
    """

    existing_set = set(tuple(map(str, combo)) for combo in existing_combinations)

    def is_combo_allowed(combo):
        combo_tuple = tuple(map(str, combo))
        if combo_tuple in existing_set:
            return False  # 完全重复的组合直接排除

        # 判断字段重复数量
        for existing_combo in existing_combinations:
            existing_tuple = tuple(map(str, existing_combo))
            if len(combo) != len(existing_tuple):
                print(f"{combo_tuple} 和 {existing_tuple} 字段数量不同")
                continue  # 字段数量不同，无法比较，跳过

            duplicate_count = sum(1 for a, b in zip(combo, existing_combo) if a == b)
            duplicate_ratio = duplicate_count / len(combo)

            if duplicate_ratio > max_duplicate_ratio:
                return False  # 超过最大重复率，拒绝该组合
        return True  # 无过高重复，允许使用

    result = [combo for combo in combinations_list if is_combo_allowed(combo)]

    # filtered_result = [
    #     combo for combo in result if len(set(combo)) == len(combo)
    # ]
    # 新增：按文件名去重，保留唯一组合
    filtered_result = []

    for combo in result:
        seen_basenames = set()
        has_duplicate = False
        # 提取每个元素的 basename
        for path in combo:
            # 判断是否为字符串，并且符合文件路径格式（包含后缀）
            if isinstance(path, str) and os.path.sep in path:
                base_name = os.path.basename(path)

                if base_name in seen_basenames:
                    has_duplicate = True
                    break
                seen_basenames.add(base_name)
        if not has_duplicate:
            filtered_result.append(combo)

    return filtered_result


if __name__ == "__main__":
    # 获取命令行参数
    existing_combinations = [["a", "1", "A"], ["a", "2", "B"]]

    combinations_list = [["a", "1", "B"], ["b", "2", "B"], ["c", "3", "A"]]

    ret = compare_combinations(combinations_list, existing_combinations, max_duplicate_ratio=(1 / 3))
    print(ret)
